local skynet = require "skynet"
local cluster = require "skynet.cluster"
local tabletool = require "tabletool"
local timetool = require "timetool"
local msghelper = require "tablehelper"
local timer = require "timer"
local filelog = require "filelog"
local itemscript = require "itemscript"
local math_floor = math.floor
local math_ceil = math.ceil

local robotbase = require "robot"

local json = require "cjson"
json.encode_sparse_array(true,1,1)

require "fishenum"

local STOCK = 0.95

local gamelogic
local seatlogic
local TableLogic = {}

function TableLogic.init(tablebase, conf, frame_time)
	if conf == nil then
		filelog.sys_error("TableLogic.init conf = nil")
		return false
	end

	local table_data = tablebase.table_data
	gamelogic = tablebase.logic.gamelogic
	seatlogic = tablebase.logic.seatlogic

	table_data.idx = conf.idx
	table_data.state = ETableState.TABLE_STATE_GAME_START

	local seat
	local count = 1
    while count <= conf.max_player_num do
    	seat = {
    		index = 0,
			rid = 0,
			state = 0,  --该坐位玩家状态
			playerinfo = {
				rolename = "",
				logo = "",
				sex = 0,
				ip_addr = "",
				diamond = 0,
			},
			items = {},
			is_tuoguan = 0,
			diamond = 0,
			-- entercarry = 0,
			currdiamond = 0,
			losediamond = 0,
			launcher = nil,
			shootlevel = conf.baseshootlevel,
			shootstat = 0,
			shootcount = 0,
    	}
    	seatlogic.init(seat, count)
    	table.insert(table_data.seats, seat) 
		count = count + 1
    end
	table_data.conf = tabletool.deepcopy(conf)
	local status, service_fee_rate = skynet.pcall(skynet.call, ".slotmgr", "lua", "get_service_fee_rate", TableLogic.get_gamename(table_data))
	if status and service_fee_rate then
		table_data.conf.service_fee_rate = service_fee_rate
	else
		table_data.conf.service_fee_rate = 0.07
	end

	gamelogic.init(tablebase, frame_time)
	return true
end

function TableLogic.checkallonline(table_data)
	local online = false
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if not seat.is_disconnect then
				online = true
				break
			end
		end
	end
	if not online then
		if table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
		for _, seat in ipairs(table_data.seats) do
			if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
				TableLogic.passive_standuptable(table_data, seat)
			end
		end
	end
	return online
end

function TableLogic.disconnect(table_data, seat)
	if seat.disconnect_timer_id and seat.disconnect_timer_id > 0 then
		return 
	end
	seat.is_tuoguan = EBOOL.TRUE
	seat.is_disconnect = true
	-- if TableLogic.checkallonline(table_data) then
		msghelper:sendmsg_to_alltableplayer("disconnectN", {
			rid = seat.rid,
			seat_index = seat.index,
			is_tuoguan = seat.is_tuoguan,
		})
	-- end
	seat.disconnect_timer_id = timer.settimer(30*100, "disconnect", {seat_index = seat.index})
end

function TableLogic.clear(table_data)
	filelog.warn("table will be deleted, room win diamond = ", table_data.roomdiamondwin)
	if table_data.timer_id > 0 then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
	end

	if table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end

	for k,v in pairs(table_data) do
		table_data[k] = nil
	end
end

function TableLogic.entertable(table_data, rid, rdata, request, seat)
	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end
	if seat then 
		if seat.is_tuoguan == EBOOL.TRUE then
			seat.is_tuoguan = EBOOL.FALSE
		end
	else
		if table_data.waits[rid] == nil then
			table_data.waits[rid] = {}
			table_data.waits[rid].playerinfo = {}
		end
		table_data.waits[rid].rid = rid
		table_data.waits[rid].playerinfo.rolename = rdata.info.rolename
		table_data.waits[rid].playerinfo.logo = rdata.info.rolename
		table_data.waits[rid].playerinfo.sex = rdata.info.sex
	end
end

function TableLogic.reentertable(table_data, request, seat)
	msghelper:sendmsg_to_alltableplayer("disconnectN", {
		rid = seat.rid,
		seat_index = seat.index,
		is_tuoguan = EBOOL.FALSE,
	})

	if seat.disconnect_timer_id and seat.disconnect_timer_id > 0 then
		timer.cleartimer(seat.disconnect_timer_id)
		seat.disconnect_timer_id = -1
	end

	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end
end

function TableLogic.sitdowntable(table_data, rid, rdata, request, seat)
	if table_data.waits[rid] then
		table_data.waits[rid] = nil
	end

	seat.state = ESeatState.SEAT_STATE_PLAYING
	seat.rid = rid
	seat.playerinfo.rolename = rdata.info.rolename
	seat.playerinfo.logo = rdata.info.logo
	seat.playerinfo.sex = rdata.info.sex
	seat.playerinfo.level = rdata.info.level
	seat.playerinfo.ip_addr = rdata.info.ip_addr or rdata.ip_addr or ''
	seat.playerinfo.gps_addr = rdata.info.gps_addr or rdata.gps_addr or ''
	seat.playerinfo.introduce = rdata.info.intro or ''
	seat.playerinfo.logoframe = rdata.info.logoframe
	seat.playerinfo.diamond = rdata.money.diamond or 0

	--[[ 不需要道具了
	local items = msghelper:callmsg_to_gatesvr("getknapsack", rid) or {}
	for k, v in pairs(items) do
		seat.items[tonumber(k)] = tabletool.deepcopy(v)
	end
	--]]
	-- 按结构提供一个默认值，以便可以触发道具功能
	seat.items = {
		[1] = {
			id = 1,
			type = 1,
			num = 1,
			scriptid = 1,
		}
	}
	seat.diamond = rdata.money.diamond
	seat.currdiamond = seat.diamond
	seat.service_fee_rate = table_data.conf.service_fee_rate
	
	if seat.rid > 0 then
		seat.is_tuoguan = EBOOL.TRUE
	end
	--增加桌子人数计数 
	table_data.sitdown_player_num = table_data.sitdown_player_num + 1
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_COMMON then
		msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.conf.game_type, table_data.sitdown_player_num, table_data.conf.is_public)
	end

	if not table_data.gaming then
		skynet.timeout(3*100, gamelogic.gamestart)
		table_data.gaming = true

		-- init gamerecords
		local now = timetool.get_time()
		table_data.gamerecords.start_time = now
		table_data.gamerecords.table_uuid = skynet.getenv("nodename") .. "_"
			.. table_data.conf.table_code .. "_"
			.. table_data.conf.create_time
		table_data.gamerecords.logname = '' .. table_data.gamerecords.table_uuid .. '_'
			.. now .. '_' .. '0' .. '_'
			.. os.date("%Y%m%d_%H%M%S", now) .. '_'
			.. table_data.conf.game_room_type
	end

	--[[不需要类型和升级了
	local fishinfo = msghelper:callmsg_to_datasvr("select_rk", seat.rid, "rs_fishinfo")
	if fishinfo and fishinfo[1] == 1 and fishinfo[2][seat.rid] then
		fishinfo = fishinfo[2][seat.rid]
	else
		fishinfo = {
			launcher_type = 1,
			launcher_level = 1,
		}
		msghelper:callmsg_to_datasvr("update_rk", seat.rid, "rs_fishinfo", 0, fishinfo)
	end
	filelog.warn("createlauncher: ", fishinfo)
	--]]

	local fishinfo = {
		launcher_type = 1,
		launcher_level = 1,
	}
	seat.launcher = gamelogic.createlauncher(rid, seat.index, fishinfo.launcher_type or 1, fishinfo.launcher_level)

	local noticemsg = {
		rid = seat.rid,
		seat = {},
		player_info = {},
	}
	msghelper:copy_seatinfo(noticemsg.seat, seat)
	msghelper:copy_playerinfo(noticemsg.player_info, seat)
	msghelper:sendmsg_to_alltableplayer("sitdowntableN", noticemsg)

	seat.launcher:sync()	-- sitdowntableN之后再同步

	-------------------------- robot --------------------------
	if rdata.info.level == 99 then
		seat.isrobot = true
		msghelper:sendmsg_to_roomsvr("change_robot_num", 1, table_data.conf.idx, table_data.conf.game_type, table_data.conf.is_public)
		
		skynet.timeout(math.random(2, 3) * 100, function()
			local robot = robotbase:new()
			robot:init(TableLogic, gamelogic)
			seat.robot = robot
			pcall(robot.auto, robot, table_data, seat)
		end)
	end

	-- 移除机器人
	--[[
	skynet.timeout(math.random(300, 500), function()
		for _, s in ipairs(table_data.seats) do
			if s.isrobot then
				if table_data.sitdown_player_num == 3 then
					if math.random(100) <= 50 then
						TableLogic.passive_standuptable(table_data, s)
					end
				elseif table_data.sitdown_player_num > 3 then
					TableLogic.passive_standuptable(table_data, s)
				end
			end
		end
	end)
	--]]
	skynet.timeout(5 * 60 * 100, function()
		if seat.isrobot then
			if math.random(100) <= 50 then
				TableLogic.passive_standuptable(table_data, seat)
			else
				local minutes = 1
				skynet.fork(function()
					skynet.sleep(60 * 100)
					if math.random(100) <= 50 + minutes * 10 then
						TableLogic.passive_standuptable(table_data, seat)
					else
						minutes = minutes + 1
					end
				end)
			end
		end
	end)
end

function TableLogic.leaveallplayers(table_data)
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			TableLogic.passive_standuptable(table_data, seat)
		end
	end
end

function TableLogic.leavetable(table_data, rid, isrobot)
	gamelogic.destroylauncher(rid)
	local leavetablentc = {rid = rid}
	msghelper:sendmsg_to_alltableplayer("leavetableN", leavetablentc)
	msghelper:sendmsg_to_roomsvr("playerleavetable", rid, table_data.conf.game_room_type, isrobot, table_data.conf.game_type)
	if isrobot then
		pcall(cluster.send, "robotnode", ".router", "request_robot_leave", "fishnode", table_data.idx, {rid})
	end

	local delete = false
	--[[
	local isallrobot = true
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if seat.rid ~= rid and not seat.isrobot then
				isallrobot = false
			end
		end
	end
	if isallrobot then
		delete = true
	end
	--]]
	
	if table_data.sitdown_player_num <= 0 then
		if not table_data.delete_table_timer_id or table_data.delete_table_timer_id == -1 then
			delete = true
		else
			delete = false
		end
	end
	if delete then
		gamelogic.onegameend()
		table_data.delete_table_timer_id = timer.settimer(table_data.conf.retain_time*100, "delete_table")
		if table_data.timer_id and table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
	end

	if table_data.waits[rid] then
		table_data.waits[rid] = nil
	end
end

function TableLogic.passive_standuptable(table_data, seat)
	if seat then
		local rid = seat.rid
		table_data.sitdown_player_num = table_data.sitdown_player_num - 1
		if table_data.sitdown_player_num < 0 then
			table_data.sitdown_player_num = 0
		end
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_COMMON then
			msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.conf.game_type, table_data.sitdown_player_num, table_data.conf.is_public)
		end
		local noticemsg = {
			rid = rid, 
			seat_index = seat.index,
			state = seat.state,
		}
		if seat.isrobot then
			msghelper:sendmsg_to_roomsvr("change_robot_num", -1, table_data.conf.idx, table_data.conf.game_type, table_data.conf.is_public, table_data.conf.room_type)
			seat.robot:set_enable()
			seat.robot:set_shootenable()
			seat.robot = nil
		end
		msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg) --通知座位上的人，某位玩家离

		local wldiamond = seat.diamond - seat.currdiamond
		if seat.losediamond ~= 0 then
			TableLogic.change_money(table_data, seat, {diamond = wldiamond})
		end

		TableLogic.leavetable(table_data, rid, seat.isrobot)
	end
	--初始化座位数据
	seatlogic.clear_seat(seat, table_data.conf)
end

function TableLogic.standuptable(table_data, seat)
	if not seat or seat.rid <= 0 then return end
	table_data.sitdown_player_num = table_data.sitdown_player_num - 1
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_COMMON then
		msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.conf.game_type, table_data.sitdown_player_num, table_data.conf.is_public)
	end
	if seat.isrobot then
		msghelper:sendmsg_to_roomsvr("change_robot_num", -1, table_data.conf.idx, table_data.conf.game_type, table_data.conf.is_public, table_data.conf.room_type)
	end
	local noticemsg = {
		rid = seat.rid, 
		seat_index = seat.index,
		state = seat.state,
	}
	msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg)
	
	local wldiamond = seat.diamond - seat.currdiamond
	if seat.losediamond ~= 0 then
		TableLogic.change_money(table_data, seat, {diamond = wldiamond})
	end

	--初始化座位数据
	seatlogic.clear_seat(seat, table_data.conf)
end

function TableLogic.upgradelauncher(table_data, seat, request)
	local launcher = seat.launcher
	if launcher then
		if request.level and request.level > 0 then
			local level = launcher.Level + 1
			if launcher.Level >= 5 then
				level = 1
			end
			gamelogic.upgradelauncher(seat.rid, seat.index, launcher, level)
		else
			local level = launcher.Level - 1
			if launcher.Level <= 1 then
				level = 5
			end
			gamelogic.upgradelauncher(seat.rid, seat.index, launcher, level)
		end
		-- msghelper:sendmsg_to_datasvr("update_rk", seat.rid, "rs_fishinfo", 0, {launcher_level = launcher.Level})
		local noticemsg = {
			rid = seat.rid, 
			seat_index = seat.index,
			level = launcher.Level,
		}
		msghelper:sendmsg_to_alltableplayer("upgradelauncherN", noticemsg)
	end
end

function TableLogic.upgradeshoot(table_data, seat, request)
	if seat then
		seat.shootlevel = seat.shootlevel + table_data.conf.upgradestage * request.level
		if request.level == 1 then
			if seat.shootlevel > table_data.conf.baseshootlevel + table_data.conf.upgradestage * (4 + table_data.conf.stageoffset) then
				seat.shootlevel = table_data.conf.baseshootlevel
			end
		else
			if seat.shootlevel < table_data.conf.baseshootlevel then
				seat.shootlevel = table_data.conf.baseshootlevel + table_data.conf.upgradestage * (4 + table_data.conf.stageoffset)
			end
		end

		local noticemsg = {
			rid = seat.rid, 
			seat_index = seat.index,
			level = seat.shootlevel,
		}
		msghelper:sendmsg_to_alltableplayer("upgradeshootN", noticemsg)
		filelog.warn("upgradeshoot: shootlevel = ", seat.shootlevel)
		TableLogic.upgradelauncher(table_data, seat, request)
	end
end

function TableLogic.useitem(table_data, seat, item, num)
	if seat.shootstat and seat.shootstat == 1 then
		seat.shootstat = 0	-- 解除锁定
		msghelper:sendmsg_to_alltableplayer("shootstatN", {
			rid = seat.rid,
            seat_index = seat.index,
            shootstat = seat.shootstat,
        })
	else
		if not item then return end
		local scriptid = item.scriptid
		if type(itemscript[scriptid]) == 'function' then
			itemscript[scriptid](seat, num)
			-- itemscript["del"](seat, item.type, num)	-- 取消删除，以便道具功能始终可以触发
		end
	end
end

function TableLogic.changediamond(table_data, seat_index, val, fishtype)
	local seat = table_data.seats[seat_index]
	if seat then
		local service_fee = 0
		if val < 0 then
			table_data.roomdiamond = table_data.roomdiamond - val*STOCK
			seat.losediamond = seat.losediamond + val	-- 捕获鱼并产生服务费时射击消耗的钱数
			seat.shootcount = seat.shootcount + 1
		else
			service_fee = math.floor(val * seat.service_fee_rate)
			table_data.roomdiamond = table_data.roomdiamond - val
			TableLogic.change_money(table_data, seat, {diamond = seat.losediamond})	-- 捕获前射击花费的流水
			seat.currdiamond = seat.diamond	-- 产生服务费，结算负流水，记录此时金钱数，供下面结算正流水用
			seat.losediamond = 0	-- 捕获鱼并产生服务费时射击消耗的钱数，结算流水要清空
		end
		table_data.roomdiamondwin = table_data.roomdiamondwin - val

		local rid_value = {beginvalue = seat.diamond}
		rid_value.value = val - service_fee
		-- diamond, 玩家在游戏中真实的数量, 也是客户端看到的数量
		seat.diamond = seat.diamond + rid_value.value
		rid_value.endvalue = seat.diamond
		seat.playerinfo.diamond = rid_value.endvalue
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_COMMON then
			msghelper:sendmsg_to_alltableplayer("moneychangeN", {money_change_list = {{
				rid = seat.rid,
				money_type = EMoneyType.DIAMOND,
				change_value = math.floor(rid_value.value),
				final_value = math.floor(rid_value.endvalue),
				target_type = fishtype,
			}}})
		end
		if val > 0 then
			TableLogic.change_money(table_data, seat, {diamond = rid_value.value, real_change = val})
			seat.currdiamond = seat.diamond	-- 产生服务费，结算流水，记录此时金钱数，供下次结算流水用
		end
	end
end

--
--  改变金钱
--
function TableLogic.change_money(table_data, seat, itemvalues)
	if seat.playerinfo.level == 99 then return end
    local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("change money error: no records")
		return false
	end
	--扣除抽水
	local rid = seat.rid
	local comment = {
		table_uuid = records.table_uuid,
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
		start_time = records.start_time or '0',
	}
	
	local item = {
		rolename = seat.playerinfo.rolename,
		level = seat.playerinfo.level,
		beginvalue = math.floor(seat.currdiamond),
		real_change = math.floor(itemvalues.real_change or itemvalues.diamond or 0),
	}
	item.value = math.floor(itemvalues.diamond)
	item.endvalue = item.beginvalue + math.floor(item.value)
	-- filelog.sys_info("change_player_money: ", item, seat.currdiamond, seat.diamond, seat.losediamond)

	local rid_values = {}
	rid_values[rid] = {
		beginvalue = item.beginvalue,
		value = item.value,
		endvalue = item.endvalue,
		real_change = item.value,
		total_bet = math.abs(item.value),
		level = seat.playerinfo.level
	}
	-- local status, result = skynet.pcall(cluster.call, "gatenode", ".router", "change_money", rid, {diamond = itemvalues.diamond}, comment)
	local status = msghelper:callmsg_to_gatesvr("brgame_balance_diamond", rid_values, comment)
    if not status then
        filelog.sys_error("change money call failed!")
        return false
	end
	TableLogic.save_gamerecords(table_data, rid, itemvalues)
	
	local total_change, total_water, sys_change, total_bet, total_income = 0, 0, 0, 0, 0
	if not seat.isrobot then
		total_change = total_change + item.value
		total_water = total_water + math.abs(item.real_change or 0)
		sys_change = sys_change + item.real_change
		--记录下注值
		local bet_logstr = string.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%s,%s,%s", EDiamondChange.DOBET, rid, item.level, 
			0, 0, 0, 0, 0, 0, comment.table_uuid, comment.start_time, comment.table_tradeid)
		skynet.send(".diamondlog", "lua", "write", bet_logstr)
		--记录流水变化值
		local real_water = item.real_change or 0
		local chg_logstr = string.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%s,%s,%s", EDiamondChange.FUWUFEI, rid, item.level, real_water, 
			item.beginvalue, item.endvalue, 0, 0, 0, comment.table_uuid, comment.start_time, comment.table_tradeid)
		if real_water ~= 0 then
			skynet.send(".diamondlog", "lua", "write_file_redis", chg_logstr, rid)
		else
			skynet.send(".diamondlog", "lua", "write", chg_logstr)
		end

		--记录系统的流水变化
        local logstr = string.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%s,%s,%s", EDiamondChange.FUWUFEI, 0, 0, -sys_change, 0, 0, 0, 0, 0,
        comment.table_uuid, comment.start_time, comment.table_tradeid)
        skynet.send(".diamondlog", "lua", "write", logstr)
	else
		if item.value ~= 0 then
			total_income = total_income + item.value
		end
	end
	if total_income ~= 0 then
		skynet.send(".slotmgr", "lua", "update_sys_income", TableLogic.get_gamename(table_data), total_income, comment)
	end
	skynet.send(".slotmgr", "lua", "handle_balance_result", TableLogic.get_gamename(table_data), total_change, total_water, total_bet, comment)
	
	return result, comment
end

function TableLogic.save_gamerecords(table_data, rid, itemvalues)
	local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("save game records error: no records")
		return
	end

	local val = itemvalues.diamond
	local logname = 'fishnode'  -- records.table_uuid
	-- filelog.debug("logname = ", logname)
	local logstr = ''
		.. (records.table_uuid or '0') .. '_'
		.. table_data.conf.room_type .. '_'
		.. timetool.get_time() .. '_'
		.. rid .. '_'
		.. (val >= 0 and 1 or 2) .. '_'
		.. (val >= 0 and val or -val)

	-- filelog.debug("logstr = ", logstr)
	skynet.call(".tablerecordlog", "lua", "write", logname, logstr)
end

function TableLogic.get_seat_currdiamond(table_data, seat_index)
	return table_data.seats[seat_index].diamond, table_data.seats[seat_index].shootlevel
end

--根据指定桌位号获得一张空座位
function TableLogic.get_emptyseat_by_index(table_data)
	for index, seat in pairs(table_data.seats) do
		if seatlogic.is_empty(seat) then
			return seat
		end
	end
	return nil
end

function TableLogic.get_seat_by_rid(table_data, rid)
	for index, seat in pairs(table_data.seats) do
		if rid == seat.rid then
			return seat
		end
	end
	return nil
end

function TableLogic.get_playing_num(table_data)
	return table_data.sitdown_player_num
end

--判断桌子是否满了
function TableLogic.is_full(table_data)
	return table_data.sitdown_player_num >= table_data.conf.max_player_num
end

--判断当前是否能够开始游戏
function TableLogic.is_canstartgame(table_data)
	return table_data.sitdown_player_num >= table_data.conf.max_player_num
end

--判断当前局是否已经结束游戏
function TableLogic.is_onegameend(table_data)
	return (not table_data.gaming)
end

function TableLogic.get_gamename(table_data)
	local gamename = "fish"
    if table_data.conf.room_type == ERoomType.ROOM_TYPE_COMMON then
        gamename = gamename .. "_diamond_".. table_data.conf.game_type
    end
    return gamename
end

function TableLogic.changestockratio(val)
	STOCK = val
end

return TableLogic
